<?php
/**
 * @link https://www.kancloud.cn/cleverstone/ymb2
 * @copyright Copyright (c) 2020 Yii Manager Software LLC
 */

namespace builder\table;

use builder\helper\App;
use builder\helper\H5;
use builder\helper\RegEx;
use builder\helper\Url;
use builder\base\Builder;
use builder\table\util\tcustom\TCustomType;
use builder\base\ViewBuilderInterface;
use builder\table\util\tfilter\FilterControlInterface;
use builder\table\util\roperation\OperationType;
use builder\table\util\thead\TheadColumnOption;
use builder\table\util\ToolbarTab;
use yii\db\QueryInterface;
use yii\helpers\ArrayHelper;
use yii\helpers\Json;
use yii\web\AssetBundle;
use yii\web\View;
use yii\base\InvalidArgumentException;
use Yii;

/**
 * 表格构建器
 * -------------------- HTML设置项 --------------------
 * @property bool $partial 是否是局部继承
 * @property string $title 表格标题
 * @property array $ajaxRoute 表格AJAX路由
 * @property array $rootLayout 表格根布局
 * @property string $className 给表格主容器追加 css 类名
 * @property-write array $css 给当前表格主容器直接设定 css 样式
 * @property-write string $cellExpandedMode 设置所有单元格默认展开方式
 * @property int $width 表格宽度
 * @property string|int $height 表格高度
 * @property float $maxHeight 表格最大高度, 设置后, 属性height将失效; 0代表不设置
 * @property integer $cellMinWidth 设置所有普通单元格的最小宽度, 其优先级低于表头属性中的 minWidth
 * @property integer $cellMaxWidth  设置所有普通单元格的最大宽度。其优先级低于表头属性中的 maxWidth
 * @property string $lineStyle 定义表格的多行样式, 如每行的高度等
 * @property-write boolean $autoSort 是否由组件自动进行前端排序。若为 false，则需自主排序，即由后端直接返回排序好的数据。
 * @property string $skin 皮肤
 * @property string $size 尺寸
 * @property boolean $even 开启间隔行样式
 * @property array $primaryKey 表格主键
 * @property array $tableHead 表格头
 * @property array $extraJs 额外的JS
 * @property array $extraCss 额外的CSS
 * @property array $extraAssetBundle 额外的静态包
 * @property array $rowOperation 行操作
 * @property array $toolbarFilter 工具栏 - 筛选
 * @property array $toolbarCustom 工具栏 - 自定义
 * @property array $toolbarTab 工具栏 - 选项卡
 * @property array $limits 每页条数的选择项
 * @property integer $limit 每页显示的条数
 * @property boolean $columnFilter 是否展示列筛选工具项
 * @property boolean $toolbarPrint 是否展示打印工具项
 * @property boolean $toolbarExports 是否展示导出工具项
 * @property boolean $toolbarRefresh 是否展示窗口刷新工具项
 * @property boolean $openTotalRow 是否开启行合计
 * @property boolean $autoRequest 是否自动发起数据请求
 * @property array $extraDataTpl 额外数据模板
 * -------------------- AJAX设置项 --------------------
 * @property array|\Closure|QueryInterface $query 查询实例或数据
 * @property array|\Closure $extraData 额外数据
 * @property array|string $orderBy 排序
 * @property array $columns 表格列
 * @property array|\Closure $columnDependency 表格列依赖
 * @property array $totalRow 自定义行合计(后端)
 * @property-read array $data 当前页数据
 * @property-read \yii\data\Pagination|null $pagination 分页实例
 * -------------------- 公共设置项 ---------------------
 * @property boolean $page 是否分页, 默认是
 * @property boolean $pageSample 是否是简单分页模版，默认否
 * @author cleverstone
 * @since ym2.0
 */
class TableBuilder extends Builder implements ViewBuilderInterface
{
    /**
     * The location of registered html or text.
     * This means the location is in the table head section.
     */
    const POS_HEAD = 1;

    /**
     * The location of registered html or text.
     * This means the location is at the end of the table section.
     */
    const POS_END = 2;

    /**
     * @var bool 是否局部继承
     * @see $partial
     * @see setPartial()
     * @see getPartial()
     */
    private $_partial = false;

    /**
     * @var array 表格切点内容
     * - key int 切点位置
     * - value array 切点html或text内容
     */
    private $_pointcutBody = [];

    /**
     * @var array 表格AJAX路由
     * - route string 路由
     * - params array 路由参数
     *
     * @see $ajaxRoute
     * @see setAjaxRoute()
     * @see getAjaxRoute()
     */
    private $_ajaxRoute = [];

    /**
     * @var string 表格标题
     * @see $title
     * @see setTitle()
     * @see getTitle()
     */
    private $_title = '';

    /**
     * @var array 根布局
     * @see $rootLayout
     * @see setRootLayout()
     * @see getRootLayout()
     */
    private $_rootLayout = [];

    /**
     * @var string 给表格主容器追加 css 类名
     * @see $className
     * @see setClassName()
     * @see getClassName()
     */
    private $_className;

    /**
     * @var array 给当前表格主容器直接设定 css 样式
     * @see $css
     * @see setCss()
     */
    private $_css = [];

    /**
     * @var string 设置所有单元格默认展开方式
     * @see $cellExpandedMode
     * @see setCellExpandedMode()
     */
    private $_cellExpandedMode = 'tips';

    /**
     * @var int 表格宽度, 默认自适应
     * @see $width
     * @see setWidth()
     * @see getWidth()
     */
    private $_width;

    /**
     * @var string|int 表格高度
     * @see $height
     * @see setHeight()
     * @see getHeight()
     */
    private $_height = 700;

    /**
     * @var int 表格最大高度, 设置后, 属性height将失效; 0代表不设置
     * @see $maxHeight
     * @see setMaxHeight()
     * @see getMaxHeight()
     */
    private $_maxHeight = 700;

    /**
     * @var string 表格多行样式, 如高度
     * @see $lineStyle
     * @see setLineStyle()
     * @see getLineStyle()
     */
    private $_lineStyle;

    /**
     * @var int 设置所有普通单元格的最小宽度, 其优先级低于表头属性中的 minWidth
     * @see $cellMinWidth
     * @see setCellMinWidth
     * @see getCellMinWidth
     */
    private $_cellMinWidth = 60;

    /**
     * @var int 设置所有普通单元格的最大宽度。其优先级低于表头属性中的 maxWidth
     * @see $cellMaxWidth
     * @see setCellMaxWidth()
     * @see getCellMaxWidth()
     */
    private $_cellMaxWidth;

    /**
     * @var bool 是否由组件自动进行前端排序。若为 false，则需自主排序，即由后端直接返回排序好的数据。
     * @see $autoSort
     * @see setAutoSort()
     */
    private $_autoSort = true;

    /**
     * @var string 设置表格边框风格。可选值：grid|line|row|nob
     * @see $skin
     * @see setSkin()
     * @see getSkin()
     */
    private $_skin = 'line';

    /**
     * @var string 设置表格其他尺寸. 可选值：sm|md|lg
     * @see $size
     * @see setSize()
     * @see getSize()
     */
    private $_size = 'md';

    /**
     * @var bool 是否开启隔行背景。
     * @see $even
     * @see setEven()
     * @see getEven()
     */
    private $_even = false;

    /**
     * @var array 表格头
     *
     * ```数据格式
     * [
     *     options1,
     *     options2,
     * ]
     *
     * ```
     * 支持的options有:
     * - field string 列字段, 支持 普通列字段，字段名：无 | 多选框字段，字段名:__checkbox | 行工具字段，字段名:__toolbar etc.
     * - title string 表格头标题(默认无) [不支持checkbox列]
     * - minWidth int 设置当前列的最小宽度80(默认60) [不支持checkbox列]
     * - maxWidth int 设置当前列的最大宽度800(默认无) [不支持checkbox列]
     * - style string 自定义单元格样式(默认无); [支持所有类型列]
     * - align string 单元格排列方式(left(默认), center, right); [不支持checkbox列]
     * - sort boolean 是否开启列的排序功能。(默认0) [只支持普通列字段]
     * - totalRow boolean 是否开启前端合计; [只支持普通列字段]
     * -------- edit --------
     * - edit boolean|string 用于对列所在的单元格开启编辑功能。可选值有：text, textarea , false; [只支持普通列字段]
     * - ctrlField string 控制字段, 当edit不为false时生效, 用于edit控制
     * - route string 路由, 当edit不为false时生效, 用于内容提交
     * - params array 路由参数, 当edit不为false时生效, 用于内容提交
     *
     * @see $tableHead
     * @see setTableHead()
     * @see getTableHead()
     */
    private $_tableHead = [];

    /**
     * @var array 单元格内容操作
     * 支持的选项有:
     * - type string 操作类型, 支持edit,switch
     * - field string 要操作的字段
     * - route string url/yii路由
     * - params array 请求参数
     *
     * @see $tableHead
     * @see setTableHead()
     * @see getTableHead()
     */
    private $_cellOperation = [];

    /**
     * @var boolean 是否开启行合计
     * @see $openTotalRow
     * @see setOpenTotalRow()
     * @see getOpenTotalRow()
     */
    private $_openTotalRow = false;

    /**
     * @var bool 是否自动发起数据请求
     * @see $autoRequest
     * @see setAutoRequest()
     * @see getAutoRequest()
     */
    private $_autoRequest = true;

    /**
     * @var array 自定义行合计(后端)
     * - key 字段
     * - value 值或闭包
     *
     * ```php
     * $this->setTotalRow([
     *      'sore' => 100,
     *      'money' => function($item, $dependency) {
     *          return 1000;
     *      }
     * ]);
     *
     * ```
     * @see $totalRow
     * @see setTotalRow()
     * @see getTotalRow()
     */
    private $_totalRow = [];

    /**
     * @var array 行操作
     * 支持的type有:
     * - page 页面
     * - modal 模态框
     * - ajax AJAX
     *
     * 支持的group有:
     * - normal 不分组
     * - more 更多
     *
     * 支持的options有:
     * - title string 页面或模态框标题
     * - icon string 操作项按钮图标, 支持bootstrap v4图标, fontawesome v4, layui 2.8图标
     * - route string 路由
     * - params array 路由参数, 默认表格主键
     * - method string 请求动作，当type为ajax时，该配置项有效
     * - width string  当前type为modal时有效; 指定modal的宽，默认700px
     * - height string 当前type为modal时有效; 指定modal的高，默认750px
     * - closeBtn boolean 是否显示底部关闭按钮, 当modal时有效
     * - target string 打开窗口: _self, _blank; type为page时, 改配置有效
     * - questionTitle string 询问标题, 当type为ajax时，该配置项有效
     * - questionDescription string 询问描述, 当type为ajax时，该配置项有效
     *  --- 扩展项 ---
     * - operationId string 操作ID, 用于动态设置操作项, 需要在`columns`中定义是否显示, 返回true则显示, 返回false则隐藏; 注意: 操作ID必须是有效的JS变量标识符;
     *  --- 隐藏项 ---
     * - __event_id string 事件ID, 系统自动生成
     * - __isRender boolean 是否渲染当前行操作项，默认true; 如果行操作项通过OperationHelper助手设置, 则该选项的值会根据RBAC动态设置。
     *
     * ```php
     * $rowOperation = [
     *      [
     *          'type' => 'modal',
     *          'group' => 'normal',
     *          'options' => [
     *              'title' => '编辑',
     *              'icon' => 'fas fa-edit',
     *              'route' => '//www.example.com',
     *              'params' => ['id', 'status' => 1, 'status_alias' => ':status'],
     *              'method' => 'GET',
     *              'width' => '100%',
     *              'height' => '100%',
     *              'operationId' => 'USER_EDIT',
     *              '__event_id' => 'ym2_table_event1',
     *          ]
     *      ],
     *      ...
     * ];
     *
     * ```
     * @see $rowOperation
     * @see setRowOperation()
     * @see getRowOperation()
     */
    private $_rowOperation = [];

    /**
     * @var array 工具栏 - 筛选(一维)
     * - title string 按钮名称
     * - icon string ICON图标, 支持bootstrap v4图标, fontawesome v4, layui 2.8图标
     * - columns array 筛选项 key: 字段  value:选项
     *
     * columns 格式key-value
     * - key string 字段
     * - value array 选项
     *
     * 支持的columns.value有:
     * - control string 控件类型 text、select、number、datetime、date、year、month、time、select_link
     * - label string 标签名称
     * - range integer 是否是区间类型, 用于日期类控件 1:是(默认) 0:否
     * - placeholder string 内容提示
     * - default string 默认值(项)
     * - class string|array 控件CSS类
     * - style string|array 控件CSS样式
     * - attribute string|array 控件属性
     * - options array 选项，用于select控件 格式：[`value` => `label`]
     * - multiple boolean 是否多选, 用于select控件, 默认false
     * - targetLink string 目标连接, 用于下拉联动控件
     * - route string 请求路由, 用于下拉联动控件, 返回格式 ['code' => 200, 'msg' => 'ok', 'data' => [ ['value' => 1, 'text' => '选项一'], ... ] ]
     *
     * @see $toolbarFilter
     * @see setToolbarFilter()
     * @see getToolbarFilter()
     */
    private $_toolbarFilter = [];

    /**
     * @var array 工具栏 - 自定义(二维)
     * - position 按钮位置：left、right、bottom
     * - title 按钮名称
     * - icon ICON图标, 支持bootstrap v4图标, fontawesome v4, layui 最新图标
     * - btnClass 按钮css类名, position为left和bottom时生效
     * - options 选项
     *
     * 支持的options选项有:
     * - type string 类型: page(页面), modal(模态框), ajax(AJAX); 默认modal
     * - route string 路由; 默认当前路由
     * - params array 参数; 默认主键
     * - method string 访问动作; type为ajax有效, 只支持GET、POST; 默认GET
     * - width string 当前type为modal时有效, 指定modal的宽;
     * - height string 当前type为modal时有效, 指定modal的高;
     * - target string 打开窗口: _self, _blank; type为page时, 改配置有效
     * - questionTitle string 询问标题, 当type为ajax时，该配置项有效
     * - questionDescription string 询问描述, 当type为ajax时，该配置项有效
     * - closeBtn boolean 底部关闭按钮, 当type为modal时, 改配置生效
     *  --- 隐藏项 ---
     * - __event_id 事件ID, 系统自动生成
     * - __mustSelectCheckbox 当params不为空时, 是否必须选择checkbox, 该参数根据params中的类型自动填充
     *
     * @see $toolbarCustom
     * @see setToolbarCustom()
     * @see getToolbarCustom()
     */
    private $_toolbarCustom = [];
    /**
     *
     * @var array 工具栏 - 选项卡
     * @see $toolbarTab
     * @see setToolbarTab()
     * @see getToolbarTab()
     */
    private $_toolbarTab = [];
    // 默认选项卡默认选中项的参数
    private $_toolbarTabDefault = [];
    /**
     * @var bool 是否展示列筛选
     * @see $columnFilter
     * @see setColumnFilter()
     * @see getColumnFilter()
     */
    private $_columnFilter = false;

    /**
     * @var bool 是否展示工具打印
     * @see $toolbarPrint
     * @see setToolbarPrint()
     * @see getToolbarPrint()
     */
    private $_toolbarPrint = false;

    /**
     * @var bool 是否展示工具导出
     * @see $toolbarExports
     * @see setToolbarExports()
     * @see getToolbarExports()
     */
    private $_toolbarExports = false;

    /**
     * @var bool 是否展示窗口刷新工具项
     * @see $toolbarRefresh
     * @see setToolbarRefresh()
     * @see getToolbarRefresh()
     */
    private $_toolbarRefresh = false;

    /**
     * @var array 额外的JS
     * ```php
     * $table->extraCss = <<<JS
     * console.log("测试一下 [setExtraCss] 方法;")
     * JS;
     *
     * ```
     * @see $extraJs
     * @see setExtraJs()
     * @see getExtraJs()
     */
    private $_extraJs = [];

    /**
     * @var array 额外的CSS
     * ```php
     * $table->extraCss = <<<CSS
     * body{background-color: #fff;}
     * CSS;
     *
     * ```
     * @see $extraCss
     * @see setExtraCss()
     * @see getExtraCss()
     */
    private $_extraCss = [];

    /**
     * @var array 额外的静态资源包
     * ```php
     * $table->extraAssetBundle = MemberAsset::class;
     *
     * ```
     * @see $extraAssetBundle
     * @see setExtraAssetBundle()
     * @see getExtraAssetBundle()
     */
    private $_extraAssetBundle = [];

    /**
     * @var bool 是否分页
     * @see $page
     * @see setPage()
     * @see getPage()
     */
    private $_page = true;

    /**
     * @var bool 是否是简略分页模版
     * @see $pageSample
     * @see setPageSample()
     * @see getPageSample()
     */
    private $_pageSample = false;

    /**
     * @var \yii\data\Pagination 数据分页实例
     */
    private $_pagination;

    /**
     * @var array 每页数据条数选择项
     * @see $limits
     * @see setLimits()
     * @see getLimits()
     */
    private $_limits = [10, 20, 50, 100];

    /**
     * @var integer 每页数据条数
     * @see $limit
     * @see setLimit()
     * @see getLimit()
     */
    private $_limit = 10;

    /**
     * @var array 表格主键
     * @see $primaryKey
     * @see setPrimaryKey()
     * @see getPrimaryKey()
     */
    private $_primaryKey = ['id'];

    /**
     * @var \Closure|array|QueryInterface 查询实例或数据
     * @see $query
     * @see setQuery()
     * @see getQuery()
     */
    private $_query;

    /**
     * @var array 当前页数据
     * @see $data
     * @see getData()
     */
    private $_data;

    /**
     * @var string|array 排序
     * @see $orderBy
     * @see setOrderBy()
     * @see getOrderBy()
     */
    private $_orderBy;

    /**
     * @var \Closure|array 额外数据
     * @see $extraData
     * @see setExtraData()
     * @see getExtraData()
     */
    private $_extraData;

    /**
     * @var array 额外数据模板
     * - tpl  string 模板文件路径
     * - cols array 字段列定义
     *   - key string 指定额外数据中的字段
     *   - value string 字段的标题
     * @see $extraDataTpl
     * @see setExtraDataTpl()
     * @see getExtraDataTpl()
     */
    private $_extraDataTpl = [];

    /**
     * @var array 表格数据列
     * @see $columns
     * @see setColumns()
     * @see getColumns()
     */
    private $_columns = [];

    /**
     * @var \Closure|array 数据列依赖
     * @see $columnDependency
     * @see setColumnDependency()
     * @see getColumnDependency()
     */
    private $_columnDependency;

    /**
     * @var int 事件计数器
     */
    private static $_eventCounter = 0;

    /**
     * 实例化
     * @param array $config
     * @return TableBuilder
     */
    public static function instance($config = [])
    {
        return new static($config);
    }

    /**
     * 构建器名称
     * @return string
     */
    public function getName()
    {
        return 'table';
    }

    /**
     * @return string the view path that may be prefixed to a relative view name.
     */
    public function getViewPath()
    {
        return __DIR__;
    }

    /**
     * 设置视图局部继承
     * @param bool $boolean
     * @return $this
     */
    public function setPartial($boolean = true)
    {
        $this->_partial = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getPartial()
    {
        return $this->_partial;
    }

    /**
     * 设置表格AJAX路由
     * @param string $route 路由
     * @param array $params 路由参数
     * @return $this
     */
    public function setAjaxRoute($route, $params = [])
    {
        $this->_ajaxRoute = [
            'route' => $route,
            'params' => (array)$params,
        ];

        return $this;
    }

    /**
     * @return array
     */
    public function getAjaxRoute()
    {
        return $this->_ajaxRoute;
    }

    /**
     * 设置表格标题
     * @param string $title 标题
     * @return $this
     */
    public function setTitle($title)
    {
        $this->_title = (string)$title;
        return $this;
    }

    /**
     * 获取表格标题
     * @return string
     */
    public function getTitle()
    {
        return $this->_title;
    }

    /**
     * 设置根布局
     * @param array $options
     * @return $this
     */
    public function setRootLayout(array $options)
    {
        if (!empty($options['class'])) {
            $this->formatUiClass($options['class']);
        } else {
            $options['class'] = '';
        }

        if (!empty($options['style'])) {
            $this->formatStyle($options['style']);
        } else {
            $options['style'] = '';
        }

        if (!empty($options['attribute'])) {
            $this->formatHtmlAttribute($options['attribute']);
        } else {
            $options['attribute'] = '';
        }

        $this->_rootLayout = $options;
        return $this;
    }

    /**
     * @return array
     */
    public function getRootLayout()
    {
        if (empty($this->_rootLayout)) {
            return [
                'style' => '',
                'class' => '',
                'attribute' => '',
            ];
        }

        return $this->_rootLayout;
    }

    /**
     * 给表格主容器追加 css 类名，以便更好地扩展表格样式
     * @param string|array $className
     * @return $this
     */
    public function setClassName($className)
    {
        $this->formatUiClass($className);
        $this->_className = $className;

        return $this;
    }

    /**
     * @return string
     */
    public function getClassName()
    {
        return $this->_className;
    }

    /**
     * 给当前表格主容器直接设定 css 样式
     * ```php
     * // 参数是数组
     * $this->setCss([
     *  '.ui-main1-table {font-size:14px;color:#353535;}',
     *  '.ui-main2-table {font-size:15px;color:#555555;}',
     * ]);
     *
     * // 参数是字符串
     * $this->setCss('.ui-main3-table {font-size:16px;color:#111111;}');
     * ```
     * @param string|array $css
     * @return $this
     */
    public function setCss($css)
    {
        $tmp = $this->_css;

        $this->_css = array_merge($tmp, (array)$css);
        return $this;
    }

    /**
     * 设置所有单元格默认展开方式
     * @param string $cellExpandedMode 支持tips 悬浮展开方式（默认）    default 多行展开方式
     * @return $this
     */
    public function setCellExpandedMode($cellExpandedMode)
    {
        $this->_cellExpandedMode = $cellExpandedMode;
        return $this;
    }

    /**
     * 设置表格宽度
     * @param integer $width 宽度
     * @return $this
     */
    public function setWidth($width)
    {
        $this->_width = (int)$width;
        return $this;
    }

    /**
     * 获取表格宽度
     * @return string
     */
    public function getWidth()
    {
        return $this->_width;
    }

    /**
     * 设置表格高度
     * @param string|int $height 像素高度
     * 设置表格容器高度，默认自适应。其他可选值的规则如下：
     * height: 315 设置固定高度
     * height: 'full-30' 设置自适应高度。这是一个特定的语法格式：full 表示铺满；后面的数字表示当前 table 之外的元素占用的高度，
     *          如：表格头部到页面最顶部加表格底部距离页面最底部的“距离和”
     * height: '#id-30' 设置相对父元素的高度自适应，其中 #id 即父元素的 ID 选择器，其计算原理和上述 full 相同。
     * @return $this
     */
    public function setHeight($height = 650)
    {
        $this->_height = $height;
        return $this;
    }

    /**
     * 获取表格高度
     * @return string
     */
    public function getHeight()
    {
        return $this->_height;
    }

    /**
     * @param int $maxHeight 设置表格最大高度, 0代表不设置, 设置后height属性失效
     * @return $this
     */
    public function setMaxHeight($maxHeight)
    {
        $this->_maxHeight = (int)$maxHeight;
        return $this;
    }

    /**
     * @return int
     */
    public function getMaxHeight()
    {
        return $this->_maxHeight;
    }

    /**
     * 设置表格多行样式,如:行高
     * ```php
     * $tableBuilder->setLineStyle([
     *      'height' => 'auto',
     * ])
     * ```
     * @param array|string $style
     * @return $this
     */
    public function setLineStyle($style)
    {
        $this->formatStyle($style);
        $this->_lineStyle = $style;

        return $this;
    }

    /**
     * @return string
     */
    public function getLineStyle()
    {
        return $this->_lineStyle;
    }

    /**
     * 设置所有普通单元格的最小宽度, 其优先级低于表头属性中的 minWidth
     * 默认: 60
     * @param int $cellMinWidth
     * @return $this
     */
    public function setCellMinWidth($cellMinWidth)
    {
        $this->_cellMinWidth = (int)$cellMinWidth;
        return $this;
    }

    /**
     * @return int
     */
    public function getCellMinWidth()
    {
        return $this->_cellMinWidth;
    }

    /**
     * 设置所有普通单元格的最大宽度。其优先级低于表头属性中的 maxWidth
     * @param int $cellMaxWidth
     * @return $this
     */
    public function setCellMaxWidth($cellMaxWidth)
    {
        $this->_cellMaxWidth = (int)$cellMaxWidth;
        return $this;
    }

    /**
     * @return int
     */
    public function getCellMaxWidth()
    {
        return $this->_cellMaxWidth;
    }

    /**
     * @param bool $autoSort 是否由组件自动进行前端排序。若为 false，则需自主排序，即由后端直接返回排序好的数据。
     * @return $this
     */
    public function setAutoSort(bool $autoSort)
    {
        $this->_autoSort = $autoSort;
        return $this;
    }

    /**
     * 设置表格皮肤
     * @param string $skin 可选值：grid|line|row|nob
     * @return $this
     */
    public function setSkin($skin)
    {
        $this->_skin = (string)$skin;
        return $this;
    }

    /**
     * @return string
     */
    public function getSkin()
    {
        return $this->_skin;
    }

    /**
     * 设置表格尺寸
     * @param string $size 可选值：sm|md|lg
     * @return $this
     */
    public function setSize($size)
    {
        $this->_size = (string)$size;
        return $this;
    }

    /**
     * @return string
     */
    public function getSize()
    {
       return $this->_size;
    }

    /**
     * 是否开启隔行背景。
     * @param bool $boolean
     * @return $this
     */
    public function setEven($boolean = true)
    {
        $this->_even = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getEven()
    {
        return $this->_even;
    }

    /**
     * 设置表格头
     * @param array $data 数据项
     * @return $this
     */
    public function setTableHead(array $data)
    {
        foreach ($data as &$value) {
            // 预处理
            if (is_callable($value)) {
                $value = call_user_func($value);
            }
            if ($value instanceof TheadColumnOption) {
                $value = $value->_toArray();
            }

            // 类型检查
            $this->throwExceptionIfNotArray($value);

            // 单元格编辑配置项
            if (
                strcmp($value['type'], '__field') === 0
                && !empty($value['edit'])
            ) {
                empty($value['params']) && $value['params'] = $this->primaryKey;
                $this->_cellOperation[] = [
                    'type' => 'edit',
                    'field' => $value['field'],
                    'route' => Url::to($value['route'], ''),
                    'params' => $value['params'],
                ];
            }

            // 开关字段配置项
            if (strcmp($value['type'], '__switch') === 0) {
                empty($value['params']) && $value['params'] = $this->primaryKey;
                // 单元格开关操作配置项
                $this->_cellOperation[] = [
                    'type' => 'switch',
                    'field' => $value['field'],
                    'route' => Url::to($value['route'], ''),
                    'params' => $value['params'],
                ];
            }

            // 格式化css样式
            if (!empty($value['style'])) {
                $this->formatStyle($value['style']);
            } else {
                $value['style'] = '';
            }
        }

        $this->_tableHead = $data;
        return $this;
    }

    /**
     * 获取表格头
     * @return array
     */
    public function getTableHead()
    {
        $tableHead = $this->_tableHead;

        if (empty($this->rowOperation)) {
            foreach ($tableHead as $key => &$item) {
                if (strcmp($item['type'], '__toolbar') === 0) {
                    unset($tableHead[$key]);
                }
            }
        }

        return $tableHead;
    }

    /**
     * 自定义行合计(后端)
     * @param array $totalRow
     * @return $this
     */
    public function setTotalRow(array $totalRow)
    {
        foreach ($totalRow as $value) {
            if (!is_scalar($value) && !is_callable($value)) {
                throw new InvalidArgumentException('Expected parameter type is scalar or callable, But the others are given. ');
            }
        }

        $this->_totalRow = $totalRow;
        return $this;
    }

    /**
     * @return array
     */
    public function getTotalRow()
    {
        return $this->_totalRow;
    }

    /**
     * 是否开启行合计
     * @param bool $boolean
     * @return $this
     */
    public function setOpenTotalRow($boolean = true)
    {
        $this->_openTotalRow = (boolean)$boolean;
        return $this;
    }

    /**
     * @return boolean
     */
    public function getOpenTotalRow()
    {
        return $this->_openTotalRow;
    }

    /**
     * 是否自动发起数据请求
     * @param bool $boolean
     * @return $this
     */
    public function setAutoRequest($boolean = true)
    {
        $this->_autoRequest = (bool)$boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getAutoRequest()
    {
        return $this->_autoRequest;
    }

    /**
     * 设置行操作
     * @param array $data 数据项
     * @return $this
     */
    public function setRowOperation(array $data)
    {
        $result = [];
        foreach ($data as $item) {
            if (is_callable($item)) {
                $item = call_user_func($item);
            }

            if ($item instanceof OperationType) {
                $item = $item->_toArray();
            }

            $this->throwExceptionIfNotArray($item);

            if (
                !isset($item['options']['__isRender'])
                || $item['options']['__isRender']
            ) {
                // 事件ID
                $item['options']['__event_id'] = $this->_generateEventId();

                $result[] = $item;
            }
        }

        $this->_rowOperation = array_merge($this->_rowOperation, $result);
        return $this;
    }

    /**
     * 获取行操作
     * @return array
     */
    public function getRowOperation()
    {
        foreach ($this->_rowOperation as &$item) {
            // 参数为空, 默认设置表格主键
            empty($item['options']['params']) && $item['options']['params'] = $this->primaryKey;
        }

        return $this->_rowOperation;
    }

    /**
     * 设置是否展示列筛选
     * @param bool $boolean
     * @return $this
     */
    public function setColumnFilter($boolean = true)
    {
        $this->_columnFilter = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getColumnFilter()
    {
        return $this->_columnFilter;
    }

    /**
     * 设置是否展示工具打印
     * @param bool $boolean
     * @return $this
     */
    public function setToolbarPrint($boolean = true)
    {
        $this->_toolbarPrint = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getToolbarPrint()
    {
        return $this->_toolbarPrint;
    }

    /**
     * 设置是否展示工具导出
     * @param bool $boolean
     * @return $this
     */
    public function setToolbarExports($boolean = true)
    {
        $this->_toolbarExports = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getToolbarExports()
    {
        return $this->_toolbarExports;
    }

    /**
     * 设置窗口刷新工具
     * @param bool $boolean
     * @return $this
     */
    public function setToolbarRefresh($boolean = true)
    {
        $this->_toolbarRefresh = (bool)$boolean;
        return $this;
    }

    /**
     * @return boolean
     */
    public function getToolbarRefresh()
    {
        return $this->_toolbarRefresh;
    }

    /**
     * 设置工具栏筛选
     * @param array $columns 筛选表单项
     * @param array $options 工具栏选项
     * @return $this
     */
    public function setToolbarFilter(array $columns, $options = [])
    {
        $existUnspread = false;
        foreach ($columns as &$item) {
            if (is_callable($item)) {
                $item = call_user_func($item);
            }
            if ($item instanceof FilterControlInterface) {
                $item = $item->_toArray();
            }

            $this->throwExceptionIfNotArray($item);

            if (!empty($item['style'])) {
                $this->formatStyle($item['style']);
            } else {
                $item['style'] = '';
            }

            if (!empty($item['class'])) {
                $this->formatUiClass($item['class']);
            } else {
                $item['class'] = '';
            }

            if (!empty($item['attribute'])) {
                $this->formatHtmlAttribute($item['attribute']);
            } else {
                $item['attribute'] = '';
            }

            // 是否存在隐藏筛选项
            if (!empty($item['unspread'])) {
                !$existUnspread && $existUnspread = true;
            }
        }

        $data = [];
        $data['title'] = !empty($options['title']) ? $options['title'] : '筛选';
        $data['icon'] = !empty($options['icon']) ? $options['icon'] : 'bi bi-funnel';
        $data['columns'] = $columns;
        $data['existUnspread'] = $existUnspread;

        $this->_toolbarFilter = $data;
        return $this;
    }

    /**
     * 获取工具栏筛选
     * @return array
     */
    public function getToolbarFilter()
    {
        return $this->_toolbarFilter;
    }

    /**
     * 设置工具栏自定义项
     * @param array $data 数据项
     * @return $this
     */
    public function setToolbarCustom(array $data)
    {
        $result = [];
        foreach ($data as $key => $item) {
            if (is_callable($item)) {
                $item = call_user_func($item);
            }

            if ($item instanceof TCustomType) {
                $item = $item->_toArray();
            }

            $this->throwExceptionIfNotArray($item);

            if (empty($item)) { /** 这里为空，有可能是因为rbac检查不通过 */
                continue;
            }

            if (!empty($item['btnClass'])) {
                $this->formatUiClass($item['btnClass']);
            } else {
                $item['btnClass'] = 'layui-btn-theme';
            }

            if (empty($item['options']) || !is_array($item['options'])) {
                throw new InvalidArgumentException('The parameter [options] is empty or not array.');
            }

            // 事件ID
            $item['options']['__event_id'] = $this->_generateEventId();

            $result[$key] = $item;
        }

        $this->_toolbarCustom = array_merge($this->_toolbarCustom, $result);
        return $this;
    }

    /**
     * 获取工具栏自定义项
     * @return array
     */
    public function getToolbarCustom()
    {
        return $this->_toolbarCustom;
    }

    /**
     * 设置工具选项卡配置项
     * @param array $data
     * @return $this
     */
    public function setToolbarTab(array $data)
    {
        $isSetDefault = false;
        foreach ($data as &$value) {
            if ($value instanceof ToolbarTab) {
                $value = $value->_toArray();
            }

            if (!is_array($value) || empty($value)) {
                throw new InvalidArgumentException('The parameter [toolbarTab] is empty or not array.');
            }

            if (empty($value['params'])) throw new InvalidArgumentException('[params] must be not empty.');

            if ($value['defaultChecked']) {
                $isSetDefault = true;
                $this->_toolbarTabDefault = $value['params'];
            }
        }

        if (!$isSetDefault) {
            foreach ($data as &$datum) {
                $datum['defaultChecked'] = true;
                $this->_toolbarTabDefault = $datum['params'];
                break;
            }
        }

        $this->_toolbarTab = $data;
        return $this;
    }

    /**
     * @return array
     */
    public function getToolbarTab()
    {
        return $this->_toolbarTab;
    }

    /**
     * 设置额外的JS
     * @param array|string $js 额外的JS脚本
     * @return $this
     */
    public function setExtraJs($js)
    {
        $js = (array)$js;
        foreach ($js as &$value) {
            if (is_callable($value)) {
                $value = call_user_func($value);
            }
        }

        $this->_extraJs = array_merge($this->_extraJs, $js);
        return $this;
    }

    /**
     * 获取额外的JS
     * @return array
     */
    public function getExtraJs()
    {
        return $this->_extraJs;
    }

    /**
     * 设置额外的CSS
     * @param array|string|callable $css 额外的CSS脚本
     * @return $this
     */
    public function setExtraCss($css)
    {
        $css = (array)$css;
        foreach ($css as &$value) {
            if (is_callable($value)) {
                $value = call_user_func($value);
            }
        }

        $this->_extraCss = array_merge($this->_extraCss, $css);
        return $this;
    }

    /**
     * 获取额外的CSS
     * @return array
     */
    public function getExtraCss()
    {
        return $this->_extraCss;
    }

    /**
     * 设置额外的静态资源包
     * @param string|array $extraAssetBundle 静态资源包
     * @return $this
     */
    public function setExtraAssetBundle($extraAssetBundle)
    {
        $extraAssetBundle = (array)$extraAssetBundle;

        // 检查
        foreach ($extraAssetBundle as $assetBundle) {
            $tempObj = new $assetBundle;
            if (!$tempObj instanceof AssetBundle) {
                throw new InvalidArgumentException($assetBundle . ' is not instance of ' . AssetBundle::class);
            }
        }

        $this->_extraAssetBundle = array_merge($this->_extraAssetBundle, $extraAssetBundle);
        return $this;
    }

    /**
     * 获取额外的静态资源包
     * @return array
     */
    public function getExtraAssetBundle()
    {
        return $this->_extraAssetBundle;
    }

    /**
     * 设置是否分页
     * @param bool $pageShow 是或否
     * @return $this
     */
    public function setPage($pageShow)
    {
        $this->_page = $pageShow;
        return $this;
    }

    /**
     * 获取是否分页
     * @return bool
     */
    public function getPage()
    {
        return $this->_page;
    }

    /**
     * 设置为简略分页模版
     * @param boolean $boolean
     * @return $this
     */
    public function setPageSample($boolean = true)
    {
        $this->_pageSample = $boolean;
        return $this;
    }

    /**
     * 获取是否是简略分页模版
     * @return boolean
     */
    public function getPageSample()
    {
        return $this->_pageSample;
    }

    /**
     * 获取分页实例
     * @return \yii\data\Pagination
     */
    public function getPagination()
    {
        return $this->_pagination;
    }

    /**
     * 设置每页数据条数选择项
     * @param array $limits
     * @return $this
     */
    public function setLimits(array $limits)
    {
        $this->_limits = $limits;
        return $this;
    }

    /**
     * 获取每页数据条数选择项
     * @return array
     */
    public function getLimits()
    {
        return $this->_limits;
    }

    /**
     * 设置每页数据条数
     * @param int $limit
     * @return $this
     */
    public function setLimit($limit)
    {
        $this->_limit = $limit;
        return $this;
    }

    /**
     * 获取每页数据条数
     * @return int
     */
    public function getLimit()
    {
        return $this->_limit;
    }

    /**
     * 设置表格主键
     * @param string|array $primaryField 主键字段
     * @return $this
     */
    public function setPrimaryKey($primaryField)
    {
        $this->_primaryKey = (array)$primaryField;
        return $this;
    }

    /**
     * 获取表格主键
     * @return array
     */
    public function getPrimaryKey()
    {
        return $this->_primaryKey;
    }

    /**
     * 设置查询
     * @param \Closure|array|QueryInterface $query 查询
     * @return $this
     */
    public function setQuery($query)
    {
        if (
            is_callable($query)
            || is_array($query)
            || $query instanceof QueryInterface
        ) {
            $this->_query = $query;
            return $this;
        }

        throw new InvalidArgumentException('Expected parameter type is callable or array or instance of QueryInterface, But the others are given. ');
    }

    /**
     * 获取查询
     * @return array|QueryInterface
     */
    public function getQuery()
    {
        static $staticQuery;
        if (isset($staticQuery)) {
            return is_object($staticQuery) ? clone $staticQuery : $staticQuery;
        }

        $query = $this->_query;
        if (is_callable($query)) {
            $query = call_user_func($query);
        }

        $staticQuery = $query;

        return is_object($query) ? clone $query : $query;
    }

    /**
     * Ajax请求,当执行`render`后,获取当前页数据
     * @return array
     */
    public function getData()
    {
        return $this->_data;
    }

    /**
     * 设置额外数据
     * @param \Closure|array $extraData
     * @return $this
     */
    public function setExtraData($extraData)
    {
        if (is_callable($extraData) || is_array($extraData)) {
            $this->_extraData = $extraData;
            return $this;
        }

        throw new InvalidArgumentException('Expected parameter type is callable or array, But the others are given.');
    }

    /**
     * 获取额外的数据
     * @return array
     */
    public function getExtraData()
    {
        static $staticExtraData;
        if (isset($staticExtraData)) {
            return $staticExtraData;
        }

        $extraData = $this->_extraData;
        if (is_callable($extraData)) {
            $extraData = call_user_func($extraData, $this);
        }

        $staticExtraData = $extraData;

        return $extraData;
    }

    /**
     * 设置额外数据展示模板
     * @param array $cols
     * @param string $tplPath
     * @return $this
     */
    public function setExtraDataTpl(array $cols, $tplPath = null)
    {
        if (!$tplPath) {
            $tplPath = __DIR__ . '/tpl/ajax.extra.php';
        }

        $this->_extraDataTpl['cols'] = $cols;
        $this->_extraDataTpl['tpl'] = $tplPath;

        return $this;
    }

    /**
     * @return array
     */
    public function getExtraDataTpl()
    {
        return $this->_extraDataTpl;
    }

    /**
     * 设置排序
     * @param string|array $orderBy 排序
     * @return $this
     */
    public function setOrderBy($orderBy)
    {
        if (is_array($orderBy) || is_string($orderBy)) {
            $this->_orderBy = $orderBy;
            return $this;
        }

        throw new InvalidArgumentException('Expected parameter type is string or array, But the others are given. ');
    }

    /**
     * 获取排序
     * @return array|string
     */
    public function getOrderBy()
    {
        return $this->_orderBy;
    }

    /**
     * 设置数据列
     * @param array $columns 数据列
     * @return $this
     */
    public function setColumns(array $columns)
    {
        $this->_columns = $columns;
        return $this;
    }

    /**
     * 获取数据列
     * @return array
     */
    public function getColumns()
    {
        return $this->_columns;
    }

    /**
     * 设置数据列依赖
     * @param \Closure|array $columnDependency 数据列依赖
     * @return $this
     */
    public function setColumnDependency($columnDependency)
    {
        if (is_callable($columnDependency) || is_array($columnDependency)) {
            $this->_columnDependency = $columnDependency;
            return $this;
        }

        throw new InvalidArgumentException('Expected parameter type is callable or array, But the others are given. ');
    }

    /**
     * 获取数据列依赖
     * @return array|\Closure
     */
    public function getColumnDependency()
    {
        return $this->_columnDependency;
    }

    /**
     * 注册表格切点
     * @param string $content 内容
     * @param int $pos 切点
     * @return $this
     */
    public function registerPointcut($content, $pos = self::POS_HEAD)
    {
        $this->_pointcutBody[$pos][] = (string)$content;
        return $this;
    }

    /**
     * 开始表格
     * @return null|string
     */
    public function beginTable()
    {
        if (empty($this->_pointcutBody[self::POS_HEAD])) {
            return null;
        }

        return implode("\n", $this->_pointcutBody[self::POS_HEAD]);
    }

    /**
     * 结束表格
     * @return null|string
     */
    public function endTable()
    {
        if (empty($this->_pointcutBody[self::POS_END])) {
            return null;
        }

        return implode("\n", $this->_pointcutBody[self::POS_END]);
    }

    /**
     * 表格渲染
     * @param boolean $onlyRenderContent 是否仅渲染内容
     * @return \yii\web\Response|string
     * @throws \yii\base\InvalidConfigException
     */
    public function render($onlyRenderContent = false)
    {
        if ($this->isAjax) {
            return $this->_renderAjax();
        } else { // 渲染HTML
            $content = $this->_renderHtml();

            // 仅内容渲染
            if ($onlyRenderContent) {
                return $content;
            }

            // 局部布局渲染(不包含: 菜单和导航栏)
            if ($this->partial) {
                return $this->_view->renderFile($this->partialPath, ['content' => $content], $this);
            }

            // 主布局渲染(包含: 菜单和导航栏)
            return $this->_view->renderFile($this->mainPath, ['content' => $content], $this);
        }
    }

    /**
     * 当前URL
     * @return string
     */
    private function _ajaxUrl()
    {
        $params = [
            $this->idParam => $this->id,
            App::params('http.ajax.identity') => 1,
        ];
        $ajaxRoute = $this->ajaxRoute;

        if (empty($ajaxRoute)) {
            return Url::current($params, '');
        } elseif (empty($ajaxRoute['route'])) {
            $params = array_replace_recursive($ajaxRoute['params'], $params);
            return Url::current($params, '');
        } else {
            $ajaxRoute['params'][0] = $ajaxRoute['route'];
            $params = array_replace_recursive($ajaxRoute['params'], $params);

            return Url::to($params, '');
        }
    }

    /**
     * 渲染HTML
     * @return string
     * @throws \yii\base\InvalidConfigException
     */
    private function _renderHtml()
    {
        if ($this->title) {
            // 注册页面TITLE
            $this->_view->title = H5::encode(App::params('admin.brandName') . ' | ' . $this->title);
        }

        // 注册JS脚本
        $this->_view->registerJs($this->_resolveJsScript(), View::POS_END);

        // 注册额外CSS脚本
        if (!empty($this->extraCss)) {
            $this->_view->registerCss(implode("\n", $this->extraCss));
        }

        // 注册额外的AssetBundle
        if (!empty($this->extraAssetBundle)) {
            foreach ($this->extraAssetBundle as $assetBundle) {
                $this->_view->registerAssetBundle($assetBundle);
            }
        }

        $content = $this->_view->render('view', [
            // view.php中参数注册
            'id' => $this->id, // 当前表格组件ID
            'rowOperation' => ArrayHelper::index($this->rowOperation, null, 'group'), // 行操作项
            'toolbarCustom' => ArrayHelper::index($this->toolbarCustom, null, 'position'), // 自定义头部工具栏(分组)
            'toolbarFilter' => $this->toolbarFilter, // 表格筛选
            'rootLayout' => $this->rootLayout,      // 根布局
            'extraDataTpl' => $this->extraDataTpl,  // 额外数据展示模板
            'toolbarTab' => $this->toolbarTab, //工具选项卡

        ], $this);

        return $content;
    }

    /**
     * 解析表格组件的JS脚本
     * @return string
     */
    private function _resolveJsScript()
    {
        $jsScripts = $this->_view->render('app', [
            // app.php中参数注册
            'id' => $this->id,                      // 当前表格组件ID
            'title' => $this->title,                // 表格标题
            'url' => $this->_ajaxUrl(),             // AJAX URL
            'page' => $this->page,                  // 是否分页
            'pageSample' => $this->pageSample,      // 是否是简略分页模版
            'className' => $this->className,        // 给表格主容器追加 css 类名，以便更好地扩展表格样式
            'css' => $this->_css,                   // 给当前表格主容器直接设定 css 样式
            'cellExpandedMode' => $this->_cellExpandedMode, // 设置所有单元格默认展开方式
            'width' => $this->width,                // 表格宽度
            'height' => $this->height,              // 表格高度
            'maxHeight' => $this->maxHeight,        // 表格最大高度(设置后height失效)
            'tableHead' => $this->tableHead,        // 表格头
            'limits' => Json::encode($this->limits),// 每页数据条数选项
            'limit' => $this->limit,                // 每页数据条数
            'columnFilter' => $this->columnFilter,  // 是否展示列筛选
            'toolbarPrint' => $this->toolbarPrint,  // 是否展示工具打印
            'toolbarExports' => $this->toolbarExports,// 是否展示工具导出
            'toolbarRefresh' => $this->toolbarRefresh,// 是否展示窗口刷新工具项
            'lineStyle' => $this->lineStyle,        // 开启设置表格多行样式
            'cellMinWidth' => $this->cellMinWidth,  // 设置所有普通单元格的最小宽度，一般用于列宽自动分配的情况。其优先级低于表头属性中的 minWidth
            'cellMaxWidth' => $this->cellMaxWidth,  // 设置所有普通单元格的最大宽度。其优先级低于表头属性中的 maxWidth
            'autoSort' => $this->_autoSort,         // 是否由组件自动进行前端排序。若为 false，则需自主排序，即由后端直接返回排序好的数据。
            'skin' => $this->skin,                  // 设置表格主题皮肤
            'size' => $this->size,                  // 设置表格尺寸
            'even' => $this->even,                  // 是否开启隔行背景。
            'openTotalRow' => $this->openTotalRow,  // 是否开启行合计
            'rowOperation' => ArrayHelper::index($this->rowOperation, null, 'group'),  // 行操作项
            'cellOperation' => ArrayHelper::index($this->_cellOperation, null, 'type'), // 单元格操作项
            'toolbarCustom' => ArrayHelper::index($this->toolbarCustom, null, 'position'), // 自定义头部工具栏(分组)
            'toolbarCustomRaw' => $this->toolbarCustom, // 自定义头部工具栏
            'toolbarFilter' => $this->toolbarFilter,// 表格筛选
            'autoRequest' => $this->autoRequest,    // 是否自动发起数据请求
            'extraJs' => $this->extraJs,//额外的JS脚本
            'toolbarTabDefault' => $this->_toolbarTabDefault,

        ], $this);

        return RegEx::js($jsScripts);
    }

    /**
     * AJAX渲染
     * @return \yii\web\Response
     */
    private function _renderAjax()
    {
        $data = $this->_resolveQuery();
        $di = $this->_resolveDependency($data);
        $totalRow = $this->_resolveTotalRow($data, $di);

        $result = [];
        foreach ($data as $item) {
            $row = [];
            foreach ($this->columns as $key => $value) {
                if (is_int($key)) { // [field]
                    $field = $value;
                    $value = '';
                    if (isset($item[$field])) {
                        $value = H5::encode($item[$field]);//注意：防止xss注入
                    }
                } elseif (is_callable($value)) { // [field => closure]
                    $field = $key;
                    // \Closure
                    $value = call_user_func($value, $item, $di); // 闭包输出请注意xss攻击
                } elseif (is_string($key) && (is_scalar($value) || $value === null || (is_object($value) && method_exists($value, '__toString')))) { // [field => scalar]
                    $field = $key;
                    $value = H5::encode(strval($value));
                } else {
                    throw new InvalidArgumentException('列定义无法识别!');
                }

                $row[$field] = $value;
            }

            $result[] = $row;
        }

        if ($this->_pagination) {
            // 分页
            $count = $this->_pagination->totalCount;
        } else {
            $count = count($result);
        }

        $responseData = ['list' => $result, 'count' => $count, 'totalRow' => $totalRow];
        if ($this->_extraData !== null) {
            // 响应额外的数据
            $responseData['extraData'] = $this->extraData;
        }

        return $this->asOk('success', $responseData);
    }

    /**
     * 解析Query
     * @return array
     */
    private function _resolveQuery()
    {
        $query = $this->query;
        if ($query instanceof QueryInterface) { // Query实例
            if ($this->page === true) {
                $countQuery = clone $query;
                // pageParam: page
                // pageSizeParam: limit
                $pages = $this->_pagination = new \yii\data\Pagination([
                    'pageSizeParam' => 'limit',
                    'totalCount' => $this->pageSample ? 1000 : $countQuery->count(),
                    'pageSizeLimit' => [1, 100],
                ]);

                $query->offset($pages->offset)->limit($pages->limit);
                if (!empty($this->orderBy)) {
                    $query->orderBy($this->orderBy);
                }

                $data = $query->all();
            } else {
                if (!empty($this->orderBy)) {
                    $query->orderBy($this->orderBy);
                }

                $data = $query->all();
            }
        } else { // 数组
            if ($this->page === true) {
                /* @var array $query */
                // pageParam: page
                // pageSizeParam: limit
                $pages = $this->_pagination = new \yii\data\Pagination([
                    'pageSizeParam' => 'limit',
                    'totalCount' => count($query),
                    'pageSizeLimit' => [1, 100],
                ]);

                $data = array_slice($query, $pages->offset, $pages->limit);
            } else {
                $data = $query;
            }
        }

        $this->_data = $data;
        return $data;
    }

    /**
     * 解析行合计
     * @param array $data 当前页数据
     * @param array $di 当前页列依赖
     * @return array
     */
    private function _resolveTotalRow($data, $di)
    {
        $result = [];
        foreach ($this->totalRow as $field => $value) {
            if (is_callable($value)) {
                $value = call_user_func($value, $data, $di);
            }

            $result[$field] = $value;
        }

        return $result;
    }

    /**
     * 列依赖解析
     * @param array $data 当前页数据
     * @return array
     */
    private function _resolveDependency($data)
    {
        // 依赖解析
        $columnDependency = $this->columnDependency;
        if (is_callable($columnDependency)) {
            $di = call_user_func($columnDependency, $data);
        } else {
            $di = $columnDependency;
        }

        return $di;
    }

    /**
     * 生成事件ID
     * @return int
     */
    private function _generateEventId()
    {
        self::$_eventCounter++;
        return 'ym2_table_event' . self::$_eventCounter;
    }
}